#include "../include/threadpool.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

/**
 * 线程工作函数
 * @param arg 线程池指针
 * @return void*
 * @note 线程工作函数，从任务队列中获取任务并执行，取任务，执行任务，检查退出
 */
void* thread_worker(void* arg)
{
    // TODO: 实现线程工作函数
    ThreadPool* pool = (ThreadPool*)arg;
    while(1)
    {
        // 要对任务队列加锁
        pthread_mutex_lock(&pool->lock);
        // 如果任务队列为空，则等待，等待条件为：任务队列为空，且停止标志位未被设置
        // 使用while循环，因为可能存在虚假唤醒
        while(pool->queue_size == 0 && !pool->stop)
        {
            pthread_cond_wait(&pool->not_empty, &pool->lock);
        }
        // 如果停止标志位被设置，则退出
        if(pool->stop)
        {
            pthread_mutex_unlock(&pool->lock);
            break;
        }
        // 从任务队列中获取任务
        // 从队列头获取任务
        ThreadTask task = pool->task_queue[pool->queue_front];
        // 更新队列头指针
        pool->queue_front = (pool->queue_front + 1) % TASK_QUEUE_SIZE;
        // 更新队列大小，队列大小减1
        pool->queue_size--;
        // 唤醒等待的线程，因为队列不满，可以继续添加任务，唤醒生产者
        pthread_cond_signal(&pool->not_full);
        // 解锁
        pthread_mutex_unlock(&pool->lock);
        // 执行任务
        // 如果任务函数不为空，则执行任务
        if(task.function)
        {
            task.function(task.arg);
        }
    }
    return NULL;
}

/**
 * 创建线程池
 * @param thread_count 线程数量
 * @return 线程池指针
 */
ThreadPool* threadpool_create(int thread_count)
{
    ThreadPool* pool = (ThreadPool*)malloc(sizeof(ThreadPool));
    if(!pool)
    {
        perror("Failed to allocate memory for thread pool");
        return NULL;
    }
    pool->thread_count = thread_count;
    pool->threads = (pthread_t*)malloc(thread_count * sizeof(pthread_t));
    if(!pool->threads)
    {
        perror("Threads created failed!");
        free(pool);
        return NULL;
    }

    pthread_mutex_init(&pool->lock, NULL);
    pthread_cond_init(&pool->not_empty, NULL);
    pthread_cond_init(&pool->not_full, NULL);
    pool->queue_front = pool->queue_rear = 0;
    pool->stop = 0;
    pool->queue_size = 0;
    memset(pool->task_queue, 0, sizeof(pool->task_queue));
    for(int i = 0; i < thread_count; i++)
    {
        if(pthread_create(&pool->threads[i], NULL, thread_worker, (void*)pool) != 0)
        {
            perror("Failed to create thread");
            free(pool);
            free(pool->threads);
            pthread_mutex_destroy(&pool->lock);
            pthread_cond_destroy(&pool->not_empty);
            pthread_cond_destroy(&pool->not_full);
            return NULL;
        }
    }
    return pool;
}

/**
 * 销毁线程池
 * @param 
 */
void threadpool_destroy(ThreadPool* pool)
{
    if(!pool)      return;
    // 加锁，设置停止标志位
    pthread_mutex_lock(&pool->lock);
    pool->stop = 1;
    // 唤醒所有等待的线程
    pthread_cond_broadcast(&pool->not_empty);
    pthread_cond_broadcast(&pool->not_full);
    pthread_mutex_unlock(&pool->lock);
    // 等待所有线程退出
    for(int i = 0; i < pool->thread_count; i++)
    {
        pthread_join(pool->threads[i], NULL);
    }
    // 释放线程池资源
    free(pool->threads);
    pthread_mutex_destroy(&pool->lock);
    pthread_cond_destroy(&pool->not_empty);
    pthread_cond_destroy(&pool->not_full);
    free(pool);
}

/**
 * 向线程池投递任务
 * @param pool 线程池指针
 * @param function 任务函数
 * @param arg 任务参数
 * @return 0 成功，-1 失败
 */
int threadpool_add(ThreadPool* pool, void (*function)(void*), void* arg)
{
    // 如果线程池为空，或者任务函数为空，则返回失败
    if(!pool || !function)  return -1;
    // 要操作任务队列，要加锁
    pthread_mutex_lock(&pool->lock);
    // 如果任务队列已满，则等待，等待条件为：任务队列已满，且停止标志位未被设置
    // 使用while循环，因为可能存在虚假唤醒
    while(pool->queue_size == TASK_QUEUE_SIZE && !pool->stop)
    {
        pthread_cond_wait(&pool->not_full, &pool->lock);
    }
    // 如果停止标志位被设置，则返回失败
    if(pool->stop)
    {
        pthread_mutex_unlock(&pool->lock);
        return -1;
    }
    // 创建任务
    ThreadTask task;
    task.function = function;
    task.arg = arg;
    // 将任务添加到任务队列
    pool->task_queue[pool->queue_rear] = task;
    // 更新队列尾指针
    pool->queue_rear = (pool->queue_rear + 1) % TASK_QUEUE_SIZE;
    // 更新队列大小
    pool->queue_size++;
    // 唤醒等待的线程，因为队列不空，可以继续取任务，唤醒消费者
    pthread_cond_signal(&pool->not_empty);
    // 解锁
    pthread_mutex_unlock(&pool->lock);
    return 0;
}
